namespace MapleRock.P2PEmulator.Test
{
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Drawing;
  using System.Drawing.Drawing2D;
  using System.Linq;
  using System.Text;

  using Core;
  using Core.Events;

  public class Visualization
  {
    private ZedGraph.GraphPane pane = null;

    private int paneWidth = 0;

    private int paneHeight = 0;

    private double peerRadiusX = 0;

    private double peerRadiusY = 0;

    public Visualization(int paneWidth, int paneHeight, Network network)
    {
      this.paneWidth = paneWidth;
      this.paneHeight = paneHeight;

      double areaRatio =
        network.Report.InputData.Geography[0].AreaLongitude /
        network.Report.InputData.Geography[0].AreaLatitude;

      this.pane = new ZedGraph.GraphPane();
      this.pane.Rect = new RectangleF(0, 0, 800, Convert.ToSingle(600 * areaRatio));
      this.pane.Legend.IsVisible = false;
      this.pane.Chart.Fill = new ZedGraph.Fill(Color.White, Color.FromArgb(255, Color.ForestGreen), 45.0f);

      this.peerRadiusX = Math.Min(
        network.Report.InputData.Geography[0].AreaLatitude,
        network.Report.InputData.Geography[0].AreaLongitude) / 500.0;
      this.peerRadiusY = this.peerRadiusX * ((double)this.paneWidth / (double)this.paneHeight);
    }

    public static void PrintGeometry(XmlInputData inputData, NetworkState state, string outDir)
    {
      double areaRatio =
        inputData.Geography[0].AreaLongitude / inputData.Geography[0].AreaLatitude;

      const double AspectRatio = 600.0 / 800.0;

      var pane = new ZedGraph.GraphPane();
      pane.Rect = new RectangleF(0, 0, 800, Convert.ToSingle(600 * areaRatio));

      pane.Legend.IsVisible = false;
      pane.Chart.Fill = new ZedGraph.Fill(Color.White, Color.FromArgb(255, Color.ForestGreen), 45.0F);

      pane.Title.Text = "Геометрия сети";
      pane.XAxis.Title.Text = "Широта";
      pane.YAxis.Title.Text = "Долгота";

      foreach (var city in state.Cities.Values)
      {
        var cityRadiusY = city.Radius * 2 / AspectRatio;

        var cityGraphObj = new ZedGraph.EllipseObj(
          city.Latitude - city.Radius,
          city.Longitude + (cityRadiusY / 2),
          city.Radius * 2,
          cityRadiusY);
        cityGraphObj.ZOrder = ZedGraph.ZOrder.B_BehindLegend;
        cityGraphObj.Border.IsVisible = false;
        pane.GraphObjList.Add(cityGraphObj);

        var text = city.Id.ToString();
        var textGraphObj = new ZedGraph.TextObj(text, city.Latitude, city.Longitude);
        textGraphObj.ZOrder = ZedGraph.ZOrder.A_InFront;
        textGraphObj.FontSpec.Fill.IsVisible = false;
        textGraphObj.FontSpec.Border.IsVisible = false;

        pane.GraphObjList.Add(textGraphObj);

        foreach (var adjacentCityId in state.CitiesAdjacency.GetAdjacentEntries(city.Id))
        {
          var adjCity = state.Cities[adjacentCityId];

          var connectionGraphObj = new ZedGraph.LineObj(
            city.Latitude, city.Longitude, adjCity.Latitude, adjCity.Longitude);
          connectionGraphObj.ZOrder = ZedGraph.ZOrder.C_BehindChartBorder;
          connectionGraphObj.Line.Color = Color.Yellow;
          connectionGraphObj.Line.Width = 5;
          pane.GraphObjList.Add(connectionGraphObj);
        }
      }

      pane.XAxis.Scale.Min = 0;
      pane.XAxis.Scale.Max = inputData.Geography[0].AreaLatitude;
      pane.YAxis.Scale.Min = 0;
      pane.YAxis.Scale.Max = inputData.Geography[0].AreaLongitude;

      pane.AxisChange();

      var img = pane.GetImage();
      img.Save(outDir + "\\geometry.png", System.Drawing.Imaging.ImageFormat.Png);
      img.Dispose();
    }

    public void Print2DMesh(Network network, string outDir, string filename)
    {
      this.pane.GraphObjList.Clear();

      Debug.Assert(
        2 == network.Report.InputData.Dimensions,
        "2 == network.Report.InputData.Dimensions");

      var titleText = string.Format(
        "Информационное пространство сети ({0} мс)",
        network.State.TimePassedMs);
      this.pane.Title.Text = titleText;
      this.pane.XAxis.Title.Text = "Объём ОЗУ";
      this.pane.YAxis.Title.Text = "Объём жёсткого диска";

      var xMax = double.MinValue;
      var yMax = double.MinValue;

      foreach (var peer in network.State.Peers.Values)
      {
        xMax = Math.Max(xMax, peer.AbsolutePosition[0]);
        yMax = Math.Max(yMax, peer.AbsolutePosition[1]);

        this.PrintPeer(
          peer.AbsolutePosition[0], 
          peer.AbsolutePosition[1], 
          Color.Red);

        foreach (var connection in peer.ConnectionTable)
        {
          var toPeer = network.State.Peers[connection.PeerId];

          var connectionGraphObj = new ZedGraph.LineObj(
            peer.AbsolutePosition[0],
            peer.AbsolutePosition[1],
            toPeer.AbsolutePosition[0],
            toPeer.AbsolutePosition[1]);

          connectionGraphObj.ZOrder = ZedGraph.ZOrder.C_BehindChartBorder;
          this.pane.GraphObjList.Add(connectionGraphObj);
        }
      }

      this.RenderMessages(network);

      this.pane.XAxis.Scale.Min = 0;
      this.pane.XAxis.Scale.Max = xMax;
      this.pane.YAxis.Scale.Min = 0;
      this.pane.YAxis.Scale.Max = yMax;

      this.pane.AxisChange();

      var img = this.pane.GetImage();
      img.Save(outDir + "\\" + filename, System.Drawing.Imaging.ImageFormat.Png);
      img.Dispose();
    }

    private void PrintPeer(
      double x,
      double y,
      Color color)
    {
      this.PrintPeer(x, y, color, 1, ZedGraph.ZOrder.C_BehindChartBorder);
    }

    private void PrintPeer(
      double x,
      double y,
      Color color,
      double scale,
      ZedGraph.ZOrder zOrder)
    {
      var ellipse = new ZedGraph.EllipseObj(
        x - (this.peerRadiusX * scale),
        y + (this.peerRadiusY * scale),
        this.peerRadiusX * 2 * scale,
        this.peerRadiusY * 2 * scale);

      ellipse.Fill = new ZedGraph.Fill(
        Color.White,
        Color.FromArgb(255, color),
        45.0f);

      ellipse.ZOrder = zOrder;

      this.pane.GraphObjList.Add(ellipse);
    }

    private void RenderMessageRoute(Network network, Message message)
    {
      var fromPeer = network.State.Peers.Values.Single(p => p.Id == message.From);
      var toPeer = network.State.Peers.Values.Single(p => p.Id == message.To);

      var connectionGraphObj = new ZedGraph.LineObj(
        fromPeer.AbsolutePosition[0],
        fromPeer.AbsolutePosition[1],
        toPeer.AbsolutePosition[0],
        toPeer.AbsolutePosition[1]);

      connectionGraphObj.Line.Width = 3;
      connectionGraphObj.Line.Color = Color.Aquamarine;

      connectionGraphObj.ZOrder = ZedGraph.ZOrder.C_BehindChartBorder;
      this.pane.GraphObjList.Add(connectionGraphObj);
    }

    private void RenderMessages(Network network)
    {
      foreach (var peer in network.State.Peers.Values)
      {
        if (peer.Incoming.Count == 0)
        {
          continue;
        }

        foreach (var message in peer.Incoming)
        {
          if (message is MessageSearchRequest)
          {
            this.RenderSearchRequest(peer, network, (MessageSearchRequest)message);
          }
        }

        foreach (var message in peer.Outgoing)
        {
          if (message is MessageSearchRequest)
          {
            this.RenderSearchRequest(peer, network, (MessageSearchRequest)message);
          }
        }
      }
    }

    private void RenderSearchRequest(Peer peer, Network network, MessageSearchRequest message)
    {
      if (peer == null)
      {
        peer = network.State.Peers.Values.Single(p => (p.Id == message.From));
      }

      this.PrintPeer(
        peer.AbsolutePosition[0],
        peer.AbsolutePosition[1],
        Color.Blue,
        2,
        ZedGraph.ZOrder.B_BehindLegend);

      var fromPeer = network.State.Peers.Values.Single(p => (p.Address == message.BackAddress));
      this.PrintPeer(
        fromPeer.AbsolutePosition[0],
        fromPeer.AbsolutePosition[1],
        Color.Magenta,
        2,
        ZedGraph.ZOrder.B_BehindLegend);

      var aspectRatio = (double)this.paneWidth / (double)this.paneHeight;
      var destination = new ZedGraph.EllipseObj(
        message.Destination[0] - message.SearchRadius,
        message.Destination[1] + (message.SearchRadius * aspectRatio),
        message.SearchRadius * 2,
        message.SearchRadius * 2 * aspectRatio);

      destination.Border.Color = Color.FromArgb(200, 0, 0, 255);
      destination.Border.Width = 2;
      destination.Border.DashOn = 5;
      destination.Border.DashOff = 1;
      destination.Border.Style = DashStyle.Dash;
      destination.Fill.IsVisible = false;

      this.pane.GraphObjList.Add(destination);
    }
  }
}
